import { Injectable, NestMiddleware } from '@nestjs/common'
import { Request, Response, NextFunction } from 'express'
import { LoggerService } from './logger.service'

/**
 * HTTP请求日志中间件
 * 记录所有HTTP请求和响应的详细信息
 */
@Injectable()
export class HttpLoggerMiddleware implements NestMiddleware {
  constructor(private readonly logger: LoggerService) {}

  use(req: Request, res: Response, next: NextFunction): void {
    const startTime = Date.now()
    const requestId = this.generateRequestId()

    // 在请求头中添加请求ID
    req.headers['x-request-id'] = requestId
    res.setHeader('X-Request-ID', requestId)

    // 记录请求开始
    this.logger.logRequest(req, `Request started`)

    // 监听响应完成事件
    res.on('finish', () => {
      const responseTime = Date.now() - startTime
      this.logger.logResponse(req, res.statusCode, responseTime)
    })

    // 监听响应关闭事件（客户端断开连接）
    res.on('close', () => {
      const responseTime = Date.now() - startTime
      if (!res.writableEnded) {
        this.logger.warn(`Request closed by client: ${req.method} ${req.originalUrl || req.url}`, {
          requestId,
          ip: req.ip,
          userAgent: req.headers['user-agent'],
          responseTime
        })
      }
    })

    next()
  }

  /**
   * 生成唯一的请求ID
   */
  private generateRequestId(): string {
    return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
  }
}

/**
 * Morgan日志格式配置
 */
export const morganFormat = {
  // 开发环境格式
  dev: ':method :url :status :res[content-length] - :response-time ms',

  // 生产环境格式
  combined:
    ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"',

  // 自定义格式
  custom:
    ':remote-addr - :remote-user [:date[iso]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" :response-time ms'
}

/**
 * Morgan日志流配置
 */
export class MorganStream {
  constructor(private readonly logger: LoggerService) {}

  write(message: string): void {
    // 移除换行符并记录HTTP日志
    this.logger.http(message.trim())
  }
}
